module Msimple_fifo
#(
   parameter                RAM_TYPE   = "REG"
  ,parameter                ADDR_WIDTH = 1
  ,parameter                DATA_WIDTH = 1
  ,parameter                DATA_DEPTH = 1
)
(
  // clk and rst
   input                    i_clk
  ,input                    i_rst_n
  // write signal
  ,input                    i_wr
  ,input  [DATA_WIDTH-1:0]  i_wdata
  ,output                   o_full
  ,output [ADDR_WIDTH:0]    o_remain
  // read signal
  ,input                    i_rd
  ,output [DATA_WIDTH-1:0]  o_rdata
  ,output                   o_empty
  ,output [ADDR_WIDTH:0]    o_use
  // unusual
  ,output                   o_overflow
  ,output                   o_underflow
  ,output                   o_wrd_err
);
//==========================================================
// ver                                            start//{{{
//==========================================================
reg       [ADDR_WIDTH-1:0]  rd_rtp_r                       ;
reg       [ADDR_WIDTH-1:0]  wr_rtp_r                       ;
reg       [ADDR_WIDTH:0]    fifo_use_r                     ;

reg                         rd_r                           ;
reg       [DATA_WIDTH-1:0]  rd_data_r                      ;

reg                         overflow_r                     ;
reg                         underflow_r                    ;

wire                        ram_rd_w                       ;
wire                        ram_wr_w                       ;
wire      [ADDR_WIDTH-1:0]  ram_addr_w                     ;
wire      [DATA_WIDTH-1:0]  ram_wdata_w                    ;
wire      [DATA_WIDTH-1:0]  ram_rdata_w                    ;
//==========================================================
// ver                                              end//}}}
//==========================================================


//==========================================================
// write                                          start//{{{
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    wr_rtp_r <= {ADDR_WIDTH{1'd0}};
  end
  else if(i_wr) begin
    if(wr_rtp_r == DATA_DEPTH - 1) begin
      wr_rtp_r <= {ADDR_WIDTH{1'd0}};
    end
    else begin
      wr_rtp_r <= wr_rtp_r + {{ADDR_WIDTH-1{1'd0}},1'd1};
    end
  end
end
//==========================================================
// write                                            end//}}}
//==========================================================

//==========================================================
// read                                           start//{{{
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rd_rtp_r <= {ADDR_WIDTH{1'd0}};
  end
  else if(i_rd) begin
    if(rd_rtp_r == DATA_DEPTH - 1) begin
      rd_rtp_r <= {ADDR_WIDTH{1'd0}};
    end
    else begin
      rd_rtp_r <= rd_rtp_r + {{ADDR_WIDTH-1{1'd0}},1'd1};
    end
  end
end

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rd_r <= 1'd0;
  end
  else begin
    rd_r <= i_rd;
  end
end

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    rd_data_r <= {DATA_WIDTH{1'd0}};
  end
  else if(rd_r) begin
    rd_data_r <= ram_rdata_w;
  end
end
//==========================================================
// read                                             end//}}}
//==========================================================

//==========================================================
// output signal                                  start//{{{
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    fifo_use_r <= {ADDR_WIDTH+1{1'd0}};
  end
  else begin
    case({i_rd,i_wr})
    2'b00   : fifo_use_r <= fifo_use_r                             ;
    2'b01   : fifo_use_r <= fifo_use_r + {{ADDR_WIDTH{1'd0}},1'd1} ;
    2'b10   : fifo_use_r <= fifo_use_r - {{ADDR_WIDTH{1'd0}},1'd1} ;
    2'b11   : fifo_use_r <= fifo_use_r                             ;
    default : fifo_use_r <= fifo_use_r                             ;
    endcase
  end
end

assign o_full               = fifo_use_r == DATA_DEPTH     ;
assign o_empty              = (|fifo_use_r) == 1'd0        ;
assign o_remain             = DATA_DEPTH - fifo_use_r      ;
assign o_use                = fifo_use_r                   ;
//==========================================================
// output signal                                    end//}}}
//==========================================================

//==========================================================
// unusual                                        start//{{{
//==========================================================
always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    overflow_r <= 1'd0;
  end
  else if((fifo_use_r == DATA_DEPTH) && i_wr) begin
    overflow_r <= 1'd1;
  end
  else if(i_rd) begin
    overflow_r <= 1'd0;
  end
end

always@(posedge i_clk) begin
  if(i_rst_n == 1'd0) begin
    underflow_r <= 1'd0;
  end
  else if(((|fifo_use_r) == 1'd0) && i_rd) begin
    underflow_r <= 1'd1;
  end
  else if(i_wr) begin
    underflow_r <= 1'd0;
  end
end

assign o_overflow           = overflow_r                   ;
assign o_underflow          = underflow_r                  ;
assign o_wrd_err            = i_wr & i_rd                  ;

//==========================================================
// unusual                                          end//}}}
//==========================================================

//==========================================================
// to ram                                         start//{{{
//==========================================================
assign ram_rd_w             = i_rd                         ;
assign ram_wr_w             = i_wr                         ;
assign ram_addr_w           = i_wr ? wr_rtp_r : rd_rtp_r   ;
assign ram_wdata_w          = i_wdata                      ;
assign o_rdata              = rd_r ? ram_rdata_w : rd_data_r;
//==========================================================
// to ram                                           end//}}}
//==========================================================

Mrf1_ram_wrap
#(
   .RAM_TYPE                ( RAM_TYPE                     )
  ,.ADDR_WIDTH              ( ADDR_WIDTH                   )
  ,.DATA_WIDTH              ( DATA_WIDTH                   )
  ,.DATA_DEPTH              ( DATA_DEPTH                   )
)
M_rf1_ram_wrap
(
   .i_clk                   ( i_clk                        )
  ,.i_rd                    ( ram_rd_w                     )
  ,.i_wr                    ( ram_wr_w                     )
  ,.i_addr                  ( ram_addr_w                   )
  ,.i_wdata                 ( ram_wdata_w                  )
  ,.o_rdata                 ( ram_rdata_w                  )
);

endmodule
